/*
 * Window.h
 *
 *	Author: stravant
 *	Description:
 *		
 */

#ifndef WINDOW_H_
#define WINDOW_H_

#include <gtk/gtk.h>
#include <cairo/cairo.h>

#include "../core/Include.h"
#include "../core/Vec.h"
#include "../ui/Input.h"
#include "../ui/Event.h"
#include "../core/Action.h"

namespace tbt {

class Widget;
class WidgetRoot;
namespace fx {class Context; }

class WindowSettings {
public:
	WindowSettings(): mW(0), mH(0), mTitle(), mDecorated(false) {}
	WindowSettings(int w, int h):
		mW(0), mH(0), mTitle(), mDecorated(false) {}
	WindowSettings(int w, int h, const String& title, bool decorated = false):
		mW(w), mH(h), mTitle(title), mDecorated(decorated) {}
	~WindowSettings() {}

	int getWidth() const {return mW;}
	int getHeight() const {return mH;}
	const String& getTitle() const {return mTitle;}
	bool getDecorated() const {return mDecorated;}
private:
	int mW, mH;
	String mTitle;
	bool mDecorated;
};

class Window: private NonCopyable, public RefCounted<Window> {
public:
	Window(const WindowSettings& w = WindowSettings());
	~Window();

	void setSize(int w, int h);
	int getWidth() const {return mSize.x(); }
	int getHeight() const {return mSize.y(); }
	const IVec& getSize() const {return mSize; }

	void setPosition(int x, int y);
	int getX() const {return mPosition.x(); }
	int getY() const {return mPosition.y(); }
	const IVec& getPosition() const {return mPosition; }

	const Input& getInput() {return mInput; }
	Ref<WidgetRoot> getRootWidget() const;
	void setRootWidget(Ref<WidgetRoot> w);

	void setTitle(const String& s);

	bool isOpen() const {return mIsOpen;}

	fx::Context* getContext() const;

	void beginWindowDrag(Mouse::Button bn, int rootx, int rooty);
private:
	//trampolines
	static gboolean onRequestClose(GtkWidget* src, GdkEvent* e, Window* data);
	static gboolean onExposeNotify(GtkWidget* src, GdkEvent* e, Window* data);
	static gboolean onConfigure(GtkWidget* src, GdkEvent* e, Window* data);
	//
	static gboolean onMouseDown(GtkWidget* src, GdkEvent* e, Window* data);
	static gboolean onMouseUp(GtkWidget* src, GdkEvent* e, Window* data);
	static gboolean onMouseEnter(GtkWidget* src, GdkEvent* e, Window* data);
	static gboolean onMouseLeave(GtkWidget* src, GdkEvent* e, Window* data);
	static gboolean onMouseWheel(GtkWidget* src, GdkEvent* e, Window* data);
	static gboolean onMouseMove(GtkWidget* src, GdkEvent* e, Window* data);
	static gboolean onKeyDown(GtkWidget* src, GdkEvent* e, Window* data);
	static gboolean onKeyUp(GtkWidget* src, GdkEvent* e, Window* data);


	//dispatchers
	void dispatchRequestClose();
	void dispatchExposeNotify(GdkEvent* e);
	void dispatchConfigureNotify(GdkEvent* e);
	void dispatchMouseDown(GdkEvent* e);
	void dispatchMouseUp(GdkEvent* e);
	void dispatchMouseEnter(GdkEvent* e);
	void dispatchMouseLeave(GdkEvent* e);
	void dispatchMouseWheel(GdkEvent* e);
	void dispatchKeyDown(GdkEvent* e);
	void dispatchKeyUp(GdkEvent* e);
	void dispatchMouseMove(GdkEvent* e);


private:
	class ActionDispatch_ButtonEvent: public Action, public MouseButtonEvent {
	public:
		ActionDispatch_ButtonEvent(Ref<Widget> trg,
			Event::Type ty, Event::Flag flags, Mouse::Button which, const IVec& pos);
		void run();
	private:
		Ref<Widget> mTarget;
	};

	class ActionDispatch_MotionEvent: public Action, public MouseEvent {
	public:
		ActionDispatch_MotionEvent(Ref<Widget> trg,
			Event::Type ty, Event::Flag flags, const IVec& pos);
		void run();
	private:
		Ref<Widget> mTarget;
	};

	class ActionDispatch_CrossingEvent: public Action, public MouseEvent {
	public:
		ActionDispatch_CrossingEvent(Ref<Widget> trg,
			Event::Type ty, Event::Flag flags, const IVec& pos);
		void run();
	private:
		Ref<Widget> mTarget;
	};

	class ActionDispatch_WheelEvent: public Action, public MouseWheelEvent {
	public:
		ActionDispatch_WheelEvent(Ref<Widget> trg,
			Event::Type ty, Event::Flag flags, const IVec& pos, int delta);
		void run();
	private:
		Ref<Widget> mTarget;
	};

	class ActionDispatch_KeyEvent: public Action, public KeyEvent {
	public:
		ActionDispatch_KeyEvent(Ref<Widget> trg,
			Event::Type ty, Event::Flag flags, Key::Code kc);
		void run();
	private:
		Ref<Widget> mTarget;
	};

private:
	bool getShouldResizeEdge(const IVec& pos, GdkWindowEdge& e);

	GdkCursor* mGdkResizeCursor;
	GdkWindowEdge mGdkCursorEdge;

	fx::Context* mContext;
	GtkWindow* mWindow;

	Input mInput;
	Ref<WidgetRoot> mRootWidget;

	IVec mPosition;
	IVec mSize;

	bool mIsOpen;
	bool mResizeCursor;
};

}

#endif /* WINDOW_H_ */
